﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Wordprocessing;
using DocumentFormat.OpenXml.Packaging;
using intelliEssay.Interop;
using System.Text.RegularExpressions;
using DRAW = DocumentFormat.OpenXml.Drawing;

namespace intelliEssay.Interop.FontServices
{

    //更新了对字体的判断，英文默认为Calibri这次，我觉得是很健壮的。。。


    #region Interop

    public enum TextType
    {
        EnglishLetter,
        ChineseWord,
        Space,
        Punctuation,
        Digit
    }

    public class FontFamily
    {
        public string AsciiTheme;
        public string Ascii;
        public string EastAsiaTheme;
        public string EastAsia;
        public string HighAnsiTheme;
        public string HighAnsi;
        public string Hint;
        public string ComplexScript;
        public string ComplexScriptTheme;

        public FontFamily()
        {
            this.ComplexScript = this.ComplexScriptTheme = this.Ascii = this.AsciiTheme = this.Hint = this.HighAnsiTheme = this.HighAnsi = this.EastAsia = this.EastAsiaTheme = "null";
        }

        public override string ToString()
        {
            return "asciitheme: " + AsciiTheme + " ascii: " + Ascii + " eastasiatheme: " + EastAsiaTheme + " eastasia: " + EastAsia +
                " highansitheme: " + HighAnsiTheme + " highansi: " + HighAnsi + " CS: " + ComplexScript + " CSTheme:" + ComplexScriptTheme + " hint: " + Hint;
            //return base.ToString();
        }
    }

    class RunInfo
    {
        public static Regex CHNTest = new Regex("[\u4e00-\u9fa5]", RegexOptions.Compiled);
        WordprocessingDocument wd;
        public string Text;
        HostApplications ha;
        public bool IsBold;
        public bool IsItalic;
        public FontFamily FontFamily = new FontFamily();
        public bool IsUnderline;
        public string FontSize;
        public string Color;
        public VerticalPositionValues VerticalPositionAlignment = new VerticalPositionValues();
        public long CharacterScale;
        public int Spacing;

        //如果有应用程序的，将DocInfo中DefaultRunInfo的构造函数改掉。
        public RunInfo(WordprocessingDocument wd)
        {
            Text = "null";
            IsBold = false;
            IsUnderline = false;
            IsItalic = false;
            FontSize = "21";
            Color = "auto";
            VerticalPositionAlignment = VerticalPositionValues.Baseline;
            CharacterScale = 0;
            Spacing = 0;
            this.wd = wd;
        }


        public override string ToString()
        {
            //return base.ToString();
            return "******** - New  Run - ********\r\n[ innertext (" + Text + ") ]\r\n[ fontsize (" + FontSize + ") ] [ IsBold (" + IsBold.ToString() + ") ] [ IsI (" + IsItalic.ToString() +
                ") ] [ IsU (" + IsUnderline.ToString() + ") ]\r\n[ color (" + Color + ")]\r\n" + "[ fontfamily (" + FontFamily.ToString() + ") ]\r\n[ VerticalTextAlignment ( " +
                VerticalPositionAlignment + " )]\r\n[ CharacterScale (" + CharacterScale + ") ]\r\n[ Spacing (" + Spacing + ") ]\r\n******** - Run Over - ********\r\n";

        }

        internal dynamic GetFont(HostApplications ha, string queryCommand)
        {
            string result = null;
            bool cultrueZhCN = false;
            switch (queryCommand)
            {
                case "asis":
                    cultrueZhCN = CHNTest.IsMatch(Text[0].ToString());
                    break;
                case "zhcn":
                    cultrueZhCN = true;
                    break;
                case "other":
                    cultrueZhCN = false;
                    break;
            }
            if (cultrueZhCN)
            {
                var eastAsiaTheme = FontFamily.EastAsiaTheme;
                if (eastAsiaTheme != null)
                {
                    if (null == DocumentElementPropertyCache.WordDocPropertyCache.GetProperty(wd, "Font-EastAsiaTheme-" + eastAsiaTheme))
                    {
                        DRAW.FontCollectionType elementList = eastAsiaTheme.Contains("major") ?
                         wd.MainDocumentPart.ThemePart.Theme.ThemeElements.FontScheme.MajorFont as DRAW.FontCollectionType :
                         wd.MainDocumentPart.ThemePart.Theme.ThemeElements.FontScheme.MinorFont as DRAW.FontCollectionType;
                        var fonts = elementList.Elements<DRAW.SupplementalFont>();
                        var ft = fonts.FirstOrDefault<DRAW.SupplementalFont>(font => font.Script == "Hans");
                        if (ft != null)
                        {
                            DocumentElementPropertyCache.WordDocPropertyCache.StoreProperty(wd, "Font-EastAsiaTheme-" + eastAsiaTheme, ft.Typeface);
                        }
                    }
                    result = DocumentElementPropertyCache.WordDocPropertyCache.GetProperty(wd, "Font-EastAsiaTheme-" + eastAsiaTheme);
                    if (result == null)
                    {
                        result = ApplicationSpecificDefaults.GetDefaultValueByAppName(ha, "Default-ChineseFont");
                    }
                    return result;
                }
                else
                {
                    return FontFamily.EastAsia;
                }
            }
            else
            {
                var asciiTheme = FontFamily.AsciiTheme;
                if (asciiTheme == null)
                {
                    if (DocumentElementPropertyCache.WordDocPropertyCache.GetProperty(wd, "Font-AsciiTheme-" + asciiTheme) == null)
                    {
                        var ft = wd.MainDocumentPart.ThemePart.Theme.ThemeElements.FontScheme.MajorFont.Elements<DRAW.LatinFont>().FirstOrDefault();
                        if (ft != null)
                        {
                            DocumentElementPropertyCache.WordDocPropertyCache.StoreProperty(wd, "Font-AsciiTheme-" + asciiTheme, ft.Typeface);
                        }
                    }
                    result = DocumentElementPropertyCache.WordDocPropertyCache.GetProperty(wd, "Font-AsciiTheme-" + asciiTheme);
                    if (result == null)
                    {
                        return ApplicationSpecificDefaults.GetDefaultValueByAppName(ha, "Default-NonChineseFont");
                    }
                    return result;
                }
                else
                {
                    return FontFamily.Ascii;
                }
            }
        }

    }
    class DocumentInfo
    {
        public WordprocessingDocument Document;
        public Dictionary<string, Style> AllStyles;
        public DocumentInfo() { }
        public DocumentInfo(WordprocessingDocument doc)
        {
            this.Document = doc;
            if (null == DocumentElementPropertyCache.WordDocPropertyCache.GetProperty(doc, "Style-Dict"))
            {
                AllStyles = new Dictionary<string, Style>();
                foreach (Style style in doc.MainDocumentPart.StyleDefinitionsPart.Styles.Elements<Style>())
                {
                    this.AllStyles.Add(style.StyleId, style);
                }
            }
            else
            {
                AllStyles = DocumentElementPropertyCache.WordDocPropertyCache.GetProperty(doc, "Style-Dict");
            }
        }
    }

    class RunInfoHelper
    {
        public DocumentInfo DocInfo;
        WordprocessingDocument doc;
        public RunInfoHelper(WordprocessingDocument doc)
        {
            this.DocInfo = new DocumentInfo(doc);
            this.doc = doc;
        }



        static void TryGetValue<T>(object o, ref T value)
        {
            //value = default(T);
            if (o == null)
                return;
            try
            {
                var prop = o.GetType().GetProperty("Val");
                var valueWrapper = prop.GetValue(o, null);
                T result = (T)valueWrapper.GetType().GetProperty("Value").GetValue(valueWrapper, null);
                if (result != null)
                {
                    value = result;
                }
            }
            catch
            {
            }
        }
        static bool TryGetValue3(object o, ref bool value)
        {
            bool result = false;
            if (o == null)
                return result;
            result = true;
            try
            {
                if (o is Bold)
                {
                    Bold bold = o as Bold;

                    if (bold.Val == null || bold.Val.Value == null || bold.Val.Value == true)
                    {
                        value = true;
                        return result;
                    }

                    else
                    {
                        value = false;
                    }

                }
                else
                {
                    Italic italic = o as Italic;
                    if (italic.Val == null || italic.Val.Value == null || italic.Val.Value == true)
                    {
                        value = true;
                        return result;
                    }

                    else
                    {
                        value = false;
                    }


                }
                return result;
            }
            catch
            {
                return false;
            }
        }

        static bool TryGetValue2(object o, ref bool value)
        {
            bool result = false;
            if (o == null)
                return result;
            try
            {
                var prop = o.GetType().GetProperty("Val");
                if (prop == null)
                {
                    result = true;
                    value = true;
                    return result;
                }
                var valueWrapper = prop.GetValue(o, null);
                bool re = (bool)valueWrapper.GetType().GetProperty("Value").GetValue(valueWrapper, null);
                if (re == null)
                {
                    result = true;
                    value = true;
                    return result;
                }
                result = true;
                value = re;
                return result;
            }
            catch
            {
                return false;
            }
        }

        static void TryGetRunFonts(RunFonts rf, FontFamily ff)
        {
            if (null == rf)
            {
                return;
            }
            else
            {
                if (null != rf.Ascii)
                {
                    ff.Ascii = rf.Ascii;
                }
                if (null != rf.AsciiTheme)
                {
                    ff.AsciiTheme = rf.AsciiTheme.ToString();
                }
                if (null != rf.EastAsia)
                {
                    ff.EastAsia = rf.EastAsia;
                }
                if (null != rf.EastAsiaTheme)
                {
                    ff.EastAsiaTheme = rf.EastAsiaTheme.ToString();
                }
                if (null != rf.HighAnsi)
                {
                    ff.HighAnsi = rf.HighAnsi;
                }
                if (null != rf.HighAnsiTheme)
                {
                    ff.HighAnsiTheme = rf.HighAnsiTheme.ToString();
                }
                if (null != rf.Hint)
                {
                    ff.Hint = rf.Hint.ToString();
                }
            }
        }

        public RunInfo GetRunInfo(Run run)
        {
            RunInfo runInfo = new RunInfo(doc);
            if (null == run)
                return runInfo;

            runInfo.Text = run.InnerText;
            if (runInfo.Text.StartsWith("S"))
            {
                int pass = 0;
            }
            bool withParagraphStyle = false;
            //List<bool> isBolds = new List<bool>();
            //List<bool> isItalics = new List<bool>();
            bool b_isboldtemp = false, b_isitalictemp = false;
            bool i_isboldtemp = false, i_isitalictemp = false;
            bool checklock = false;

            Dictionary<string, int> isBolds = new Dictionary<string, int>();
            Dictionary<string, int> isItalics = new Dictionary<string, int>();

            #region Pr5 Default

            runInfo.Color = ((Color)DocumentSpecificDefault.GetDefaultOrSettingByPropertyName("Color", null, this.DocInfo.Document)).Val.Value;
            runInfo.FontSize = ((FontSize)DocumentSpecificDefault.GetDefaultOrSettingByPropertyName("FontSize", null, this.DocInfo.Document)).Val.Value;
            runInfo.Spacing = ((Spacing)DocumentSpecificDefault.GetDefaultOrSettingByPropertyName("Spacing", null, this.DocInfo.Document)).Val.Value;
            runInfo.VerticalPositionAlignment = ((VerticalTextAlignment)DocumentSpecificDefault.GetDefaultOrSettingByPropertyName("VerticalTextAlignment", null, this.DocInfo.Document)).Val.Value;
            runInfo.IsUnderline = ((Underline)DocumentSpecificDefault.GetDefaultOrSettingByPropertyName("IsUnderline", null, this.DocInfo.Document)).Val.Value != UnderlineValues.None;
            runInfo.IsBold = ((Bold)DocumentSpecificDefault.GetDefaultOrSettingByPropertyName("Bold", null, this.DocInfo.Document)).Val.Value;
            runInfo.IsItalic = ((Italic)DocumentSpecificDefault.GetDefaultOrSettingByPropertyName("Italic", null, this.DocInfo.Document)).Val.Value;

            isBolds.Add("default", runInfo.IsBold ? 1 : -1);
            isItalics.Add("default", runInfo.IsItalic ? 1 : -1);

            #endregion

            #region Pr4 Table
            Table tb = null;
            try
            {
                tb = run.Parent.Parent as Table;
            }
            catch
            {
            }

            if (tb != null)
            {
                TableProperties tp = tb.ChildElements.FirstOrDefault<OpenXmlElement>(o =>
                {
                    return o.LocalName == "tblPr";
                }) as TableProperties;

                if (tp != null)
                {
                    if (tp.TableStyle != null && tp.TableStyle.Val != null)
                    {
                        Style style = DocInfo.AllStyles[tp.TableStyle.Val];
                        StyleRunProperties runProperties;
                        string styleId = string.Empty;
                        Stack<string> styles = new Stack<string>();
                        styles.Push(tp.TableStyle.Val);
                        while (style.BasedOn != null)
                        {
                            TryGetValue<string>(style.BasedOn, ref styleId);
                            if (string.Empty != styleId)
                            {
                                style = DocInfo.AllStyles[styleId];
                                styles.Push(style.StyleId);

                            }
                        }

                        while (styles.Count != 0)
                        {
                            style = DocInfo.AllStyles[styles.Pop()];
                            runProperties = style.StyleRunProperties;
                            if (null != runProperties)
                            {
                                TryGetValue<string>(runProperties.Color, ref runInfo.Color);
                                TryGetValue<string>(runProperties.FontSize, ref runInfo.FontSize);
                                TryGetValue<VerticalPositionValues>(runProperties.VerticalTextAlignment, ref runInfo.VerticalPositionAlignment);
                                TryGetValue<int>(runProperties.Spacing, ref runInfo.Spacing);
                                TryGetValue<long>(runProperties.CharacterScale, ref runInfo.CharacterScale);
                                TryGetRunFonts(runProperties.RunFonts, runInfo.FontFamily);


                                runInfo.IsUnderline = runProperties.Underline != null;

                                b_isboldtemp = TryGetValue3(runProperties.Bold, ref i_isboldtemp);
                                b_isitalictemp = TryGetValue3(runProperties.Italic, ref i_isitalictemp);
                                if (isBolds.ContainsKey("table"))
                                    isBolds["table"] = b_isboldtemp ? (i_isboldtemp ? 1 : -1) : 0;
                                else
                                    isBolds.Add("table", b_isboldtemp ? (i_isboldtemp ? 1 : -1) : 0);
                                if (isItalics.ContainsKey("table"))
                                    isItalics["table"] = b_isitalictemp ? (i_isitalictemp ? 1 : -1) : 0;
                                else
                                    isItalics.Add("table", b_isitalictemp ? (i_isboldtemp ? 1 : -1) : 0);


                            }
                        }


                    }
                }
            }

            #endregion


            #region Pr2.5 LastParagraph
            checklock = false;
            Paragraph lastParagraph = null;
            try
            {
                Paragraph thisParagraph = run.Parent as Paragraph;
                lastParagraph = thisParagraph.PreviousSibling<Paragraph>();
            }
            catch
            {
            }
            if (lastParagraph != null && lastParagraph.ParagraphProperties != null)
            {
                if (lastParagraph.ParagraphProperties.ParagraphStyleId != null)
                {
                    Stack<string> styles = new Stack<string>();
                    Style style = DocInfo.AllStyles[lastParagraph.ParagraphProperties.ParagraphStyleId.Val.Value];
                    if (style.NextParagraphStyle != null && style.NextParagraphStyle.Val != null && style.NextParagraphStyle.Val.Value != null)
                        style = DocInfo.AllStyles[style.NextParagraphStyle.Val.Value];
                    styles.Push(style.StyleId);
                    string styleId = string.Empty;
                    StyleRunProperties runProperties;
                    while (style.BasedOn != null)
                    {
                        TryGetValue<string>(style.BasedOn, ref styleId);
                        if (string.Empty != styleId)
                        {
                            style = DocInfo.AllStyles[styleId];
                            styles.Push(style.StyleId);

                        }
                    }

                    while (styles.Count != 0)
                    {
                        style = DocInfo.AllStyles[styles.Pop()];
                        if (style.StyleRunProperties != null)
                        {
                            runProperties = style.StyleRunProperties;

                            TryGetValue<string>(runProperties.Color, ref runInfo.Color);
                            TryGetValue<string>(runProperties.FontSize, ref runInfo.FontSize);
                            TryGetValue<VerticalPositionValues>(runProperties.VerticalTextAlignment, ref runInfo.VerticalPositionAlignment);
                            TryGetValue<int>(runProperties.Spacing, ref runInfo.Spacing);
                            TryGetValue<long>(runProperties.CharacterScale, ref runInfo.CharacterScale);
                            TryGetRunFonts(runProperties.RunFonts, runInfo.FontFamily);
                            runInfo.IsUnderline = runProperties.Underline != null;

                            b_isboldtemp = TryGetValue3(runProperties.Bold, ref i_isboldtemp);
                            b_isitalictemp = TryGetValue3(runProperties.Italic, ref i_isitalictemp);
                            if (isBolds.ContainsKey("paragraph"))
                                isBolds["paragraph"] = b_isboldtemp ? (i_isboldtemp ? 1 : -1) : 0;
                            else
                                isBolds.Add("paragraph", b_isboldtemp ? (i_isboldtemp ? 1 : -1) : 0);
                            if (isItalics.ContainsKey("paragraph"))
                                isItalics["paragraph"] = b_isitalictemp ? (i_isitalictemp ? 1 : -1) : 0;
                            else
                                isItalics.Add("paragraph", b_isitalictemp ? (i_isboldtemp ? 1 : -1) : 0);
                        }
                    }
                }
            }

            #endregion


            #region Pr3 Paragraph
            Paragraph pa = null;
            try
            {
                pa = run.Parent as Paragraph;
            }
            catch
            {
            }
            if (pa != null && pa.ParagraphProperties != null)
            {
                if (pa.ParagraphProperties.ParagraphStyleId != null)
                {
                    Stack<string> styles = new Stack<string>();
                    Style style = DocInfo.AllStyles[pa.ParagraphProperties.ParagraphStyleId.Val.Value];
                    styles.Push(style.StyleId);
                    StyleRunProperties runProperties;
                    string styleId = string.Empty;
                    while (style.BasedOn != null)
                    {
                        TryGetValue<string>(style.BasedOn, ref styleId);
                        if (styleId != string.Empty)
                        {
                            withParagraphStyle = true;
                            style = DocInfo.AllStyles[styleId];
                            styles.Push(style.StyleId);
                        }
                    }

                    while (styles.Count != 0)
                    {
                        style = DocInfo.AllStyles[styles.Pop()];
                        runProperties = style.StyleRunProperties;
                        if (null != runProperties)
                        {
                            TryGetValue<string>(runProperties.Color, ref runInfo.Color);
                            TryGetValue<string>(runProperties.FontSize, ref runInfo.FontSize);
                            TryGetValue<VerticalPositionValues>(runProperties.VerticalTextAlignment, ref runInfo.VerticalPositionAlignment);
                            TryGetValue<int>(runProperties.Spacing, ref runInfo.Spacing);
                            TryGetValue<long>(runProperties.CharacterScale, ref runInfo.CharacterScale);
                            TryGetRunFonts(runProperties.RunFonts, runInfo.FontFamily);
                            runInfo.IsUnderline = runProperties.Underline != null;
                            if (isBolds.ContainsKey("paragraph"))
                                isBolds["paragraph"] = b_isboldtemp ? (i_isboldtemp ? 1 : -1) : 0;
                            else
                                isBolds.Add("paragraph", b_isboldtemp ? (i_isboldtemp ? 1 : -1) : 0);
                            if (isItalics.ContainsKey("paragraph"))
                                isItalics["paragraph"] = b_isitalictemp ? (i_isitalictemp ? 1 : -1) : 0;
                            else
                                isItalics.Add("paragraph", b_isitalictemp ? (i_isboldtemp ? 1 : -1) : 0);
                        }
                    }
                }
            }
            #endregion


            #region Pr2 RunStyle
            checklock = false;
            if (run != null)
            {
                if (run.RunProperties != null && run.RunProperties.RunStyle != null)
                {
                    Style style = DocInfo.AllStyles[run.RunProperties.RunStyle.Val.Value];
                    StyleRunProperties runProperties;
                    string styleId = string.Empty;
                    Stack<string> styles = new Stack<string>();
                    styles.Push(style.StyleId);
                    TryGetValue<string>(style.BasedOn, ref styleId);
                    while (style.BasedOn != null)
                    {
                        style = DocInfo.AllStyles[styleId];
                        styles.Push(style.StyleId);

                    }

                    while (styles.Count != 0)
                    {
                        style = DocInfo.AllStyles[styles.Pop()];
                        runProperties = style.StyleRunProperties;
                        if (null != runProperties)
                        {
                            TryGetValue<string>(runProperties.Color, ref runInfo.Color);
                            TryGetValue<string>(runProperties.FontSize, ref runInfo.FontSize);
                            TryGetValue<VerticalPositionValues>(runProperties.VerticalTextAlignment, ref runInfo.VerticalPositionAlignment);
                            TryGetValue<int>(runProperties.Spacing, ref runInfo.Spacing);
                            TryGetValue<long>(runProperties.CharacterScale, ref runInfo.CharacterScale);
                            TryGetRunFonts(runProperties.RunFonts, runInfo.FontFamily);
                            runInfo.IsUnderline = runProperties.Underline != null;
                            b_isboldtemp = TryGetValue3(runProperties.Bold, ref i_isboldtemp);
                            b_isitalictemp = TryGetValue3(runProperties.Italic, ref i_isitalictemp);
                            if (isBolds.ContainsKey("run"))
                                isBolds["run"] = b_isboldtemp ? (i_isboldtemp ? 1 : -1) : 0;
                            else
                                isBolds.Add("run", b_isboldtemp ? (i_isboldtemp ? 1 : -1) : 0);
                            if (isItalics.ContainsKey("run"))
                                isItalics["run"] = b_isitalictemp ? (i_isitalictemp ? 1 : -1) : 0;
                            else
                                isItalics.Add("run", b_isitalictemp ? (i_isboldtemp ? 1 : -1) : 0);
                        }
                    }

                }


            }
            #endregion

            #region Pr1.5 RunProperties
            bool isBold = false, isItalic = false;
            bool isBoldDirect = false, isItalicDirect = false;
            RunProperties rp = run.RunProperties;
            if (rp != null)
            {
                TryGetValue<string>(rp.Color, ref runInfo.Color);
                TryGetValue<string>(rp.FontSize, ref runInfo.FontSize);
                TryGetValue<VerticalPositionValues>(rp.VerticalTextAlignment, ref runInfo.VerticalPositionAlignment);
                TryGetValue<int>(rp.Spacing, ref runInfo.Spacing);
                TryGetValue<long>(rp.CharacterScale, ref runInfo.CharacterScale);
                TryGetRunFonts(rp.RunFonts, runInfo.FontFamily);
                runInfo.IsUnderline = rp.Underline != null;

                //单独处理Bold 和 Italic

                isBoldDirect = TryGetValue3(rp.Bold, ref isBold);
                isItalicDirect = TryGetValue3(rp.Italic, ref isItalic);


            }

            if (isBoldDirect)
            {
                runInfo.IsBold = isBold;
            }
            else
            {
                if (false)//isBolds["default"] != 0)
                {
                    runInfo.IsBold = isBolds["default"] == 1;
                }
                else
                {
                    int trues = 0;
                    trues += isBolds.ContainsKey("table") ? ((isBolds["table"] == -1) ? 0 : isBolds["table"]) : 0;
                    trues += isBolds.ContainsKey("paragraph") ? ((isBolds["paragraph"] == -1) ? 0 : isBolds["paragraph"]) : 0;
                    trues += isBolds.ContainsKey("run") ? ((isBolds["run"] == -1) ? 0 : isBolds["run"]) : 0;
                    runInfo.IsBold = trues % 2 == 1;
                }
            }
            if (isItalicDirect)
            {
                runInfo.IsItalic = isItalic;
            }
            else
            {
                if (false)//isItalics["default"] != 0)
                {
                    runInfo.IsItalic = isItalics["default"] == 1;
                }
                else
                {
                    int trues = 0;
                    trues += isItalics.ContainsKey("table") ? ((isItalics["table"] == -1) ? 0 : isItalics["table"]) : 0;
                    trues += isItalics.ContainsKey("paragraph") ? ((isItalics["paragraph"] == -1) ? 0 : isItalics["paragraph"]) : 0;
                    trues += isItalics.ContainsKey("run") ? ((isItalics["run"] == -1) ? 0 : isItalics["run"]) : 0;
                    runInfo.IsItalic = trues % 2 == 1;
                }
            }
            #endregion


            return runInfo;

        }
    }


    #endregion
}
